-- drop function if exists sm_sc.fv_opr_transpose_3d(anyarray, int[2]);
create or replace function sm_sc.fv_opr_transpose_3d
(
  i_array_3d      anyarray      ,
  i_dims          int[2]        -- array[1, 2] === array[2, 1], array[1, 3] === array[3, 1] or array[2, 3] === array[3, 2]
)
returns anyarray
as
$$
declare
  v_ret     i_array_3d%type;
  v_cur_1   int   ;
  v_cur_2   int   ;
begin
  if array_ndims(i_array_3d) <> 3
  then 
    raise exception 'ndims of i_array_3d should be 3.';
  end if;
  
  if i_dims[1] not in (1, 2, 3) 
    or i_dims[2] not in (1,2,3)
    or i_dims[1] = i_dims[2]
  then 
    raise exception 'illegal i_dims';
  end if;
 
  if i_dims in (array[1, 2], array[2, 1])
  then 
    -- -- return 
    -- -- (
    -- --   with 
    -- --   cte_slice_x as 
    -- --   (
    -- --     select 
    -- --       a_cur_z,
    -- --       a_cur_y,
    -- --       array_agg(i_array_3d[a_cur_z][a_cur_y][a_cur_x] order by a_cur_x) as a_slice_x
    -- --     from generate_series(1, array_length(i_array_3d, 1)) tb_a_cur_z(a_cur_z)
    -- --       , generate_series(1, array_length(i_array_3d, 2)) tb_a_cur_y(a_cur_y)
    -- --       , generate_series(1, array_length(i_array_3d, 3)) tb_a_cur_x(a_cur_x)
    -- --     group by a_cur_z, a_cur_y
    -- --   ),
    -- --   cte_slice_z_x as 
    -- --   (
    -- --     select 
    -- --       a_cur_y, 
    -- --       array_agg(a_slice_x order by a_cur_z) as a_slice_z_x
    -- --     from cte_slice_x
    -- --     group by a_cur_y
    -- --   )
    -- --   select 
    -- --     array_agg(a_slice_z_x order by a_cur_y)
    -- --   from cte_slice_z_x
    -- -- );
    v_ret := array_fill(nullif(i_array_3d[1][1][1], i_array_3d[1][1][1]), array[array_length(i_array_3d, 2), array_length(i_array_3d, 1), array_length(i_array_3d, 3)]);
    for v_cur_1 in 1 .. array_length(i_array_3d, 2)
    loop 
      for v_cur_2 in 1 .. array_length(i_array_3d, 1)
      loop 
        v_ret[v_cur_1 : v_cur_1][v_cur_2 : v_cur_2][ : ] := i_array_3d[v_cur_2 : v_cur_2][v_cur_1 : v_cur_1][ : ];
      end loop;
    end loop;
    return v_ret;
    
  elsif i_dims in (array[1, 3], array[3, 1])
  then 
    -- -- return 
    -- -- (
    -- --   with 
    -- --   cte_slice_z as 
    -- --   (
    -- --     select 
    -- --       a_cur_x,
    -- --       a_cur_y,
    -- --       array_agg(i_array_3d[a_cur_z][a_cur_y][a_cur_x] order by a_cur_z) as a_slice_z
    -- --     from generate_series(1, array_length(i_array_3d, 1)) tb_a_cur_z(a_cur_z)
    -- --       , generate_series(1, array_length(i_array_3d, 2)) tb_a_cur_y(a_cur_y)
    -- --       , generate_series(1, array_length(i_array_3d, 3)) tb_a_cur_x(a_cur_x)
    -- --     group by a_cur_x, a_cur_y
    -- --   ),
    -- --   cte_slice_y_x as 
    -- --   (
    -- --     select 
    -- --       a_cur_x, 
    -- --       array_agg(a_slice_z order by a_cur_y) as a_slice_y_z
    -- --     from cte_slice_z
    -- --     group by a_cur_x
    -- --   )
    -- --   select 
    -- --     array_agg(a_slice_y_z order by a_cur_x)
    -- --   from cte_slice_y_x
    -- -- );
    v_ret := array_fill(nullif(i_array_3d[1][1][1], i_array_3d[1][1][1]), array[array_length(i_array_3d, 3), array_length(i_array_3d, 2), array_length(i_array_3d, 1)]);
    for v_cur_1 in 1 .. array_length(i_array_3d, 3)
    loop 
      for v_cur_2 in 1 .. array_length(i_array_3d, 1)
      loop 
        v_ret[v_cur_1 : v_cur_1][ : ][v_cur_2 : v_cur_2] := i_array_3d[v_cur_2 : v_cur_2][ : ][v_cur_1 : v_cur_1];
      end loop;
    end loop;
    return v_ret;
    
  elsif i_dims in (array[2, 3], array[3, 2])
  then 
    -- -- return 
    -- -- (
    -- --   with 
    -- --   cte_slice_y as 
    -- --   (
    -- --     select 
    -- --       a_cur_z,
    -- --       a_cur_x,
    -- --       array_agg(i_array_3d[a_cur_z][a_cur_y][a_cur_x] order by a_cur_y) as a_slice_y
    -- --     from generate_series(1, array_length(i_array_3d, 1)) tb_a_cur_z(a_cur_z)
    -- --       , generate_series(1, array_length(i_array_3d, 2)) tb_a_cur_y(a_cur_y)
    -- --       , generate_series(1, array_length(i_array_3d, 3)) tb_a_cur_x(a_cur_x)
    -- --     group by a_cur_z, a_cur_x
    -- --   ),
    -- --   cte_slice_x_y as 
    -- --   (
    -- --     select 
    -- --       a_cur_z, 
    -- --       array_agg(a_slice_y order by a_cur_x) as a_slice_x_y
    -- --     from cte_slice_y
    -- --     group by a_cur_z
    -- --   )
    -- --   select 
    -- --     array_agg(a_slice_x_y order by a_cur_z)
    -- --   from cte_slice_x_y
    -- -- );
    v_ret := array_fill(nullif(i_array_3d[1][1][1], i_array_3d[1][1][1]), array[array_length(i_array_3d, 1), array_length(i_array_3d, 3), array_length(i_array_3d, 2)]);
    for v_cur_1 in 1 .. array_length(i_array_3d, 3)
    loop 
      for v_cur_2 in 1 .. array_length(i_array_3d, 2)
      loop 
        v_ret[ : ][v_cur_1 : v_cur_1][v_cur_2 : v_cur_2] := i_array_3d[ : ][v_cur_2 : v_cur_2][v_cur_1 : v_cur_1];
      end loop;
    end loop;
    return v_ret;
    
  end if;
end
$$
language plpgsql stable
parallel safe
cost 100;
-- -- set search_path to sm_sc;
-- select sm_sc.fv_opr_transpose_3d
--   (
--     array
--     [
--       [
--         [1, 2, 3, 4],
--         [5, 6, 7, 8],
--         [9, 10, 11, 12]
--       ],
--       [
--         [21, 22, 23, 24],
--         [25, 26, 27, 28],
--         [29, 30, 31, 32]
--       ]
--     ]
--     , array[1, 2]
--   );
-- select sm_sc.fv_opr_transpose_3d
--   (
--     array
--     [
--       [
--         [1, 2, 3, 4],
--         [5, 6, 7, 8],
--         [9, 10, 11, 12]
--       ],
--       [
--         [21, 22, 23, 24],
--         [25, 26, 27, 28],
--         [29, 30, 31, 32]
--       ]
--     ]
--     , array[1, 3]
--   );
-- select sm_sc.fv_opr_transpose_3d
--   (
--     array
--     [
--       [
--         [1, 2, 3, 4],
--         [5, 6, 7, 8],
--         [9, 10, 11, 12]
--       ],
--       [
--         [21, 22, 23, 24],
--         [25, 26, 27, 28],
--         [29, 30, 31, 32]
--       ]
--     ]
--     , array[2, 3]
--   );